Installing and importing the libraries

In [1]:
import os
import cv2
import pandas as pd
import matplotlib as mat
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from skimage.io import imread
%matplotlib inline
pd.options.display.max_colwidth = 100

import random

from numpy.random import seed
seed(50)

from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.metrics import accuracy_score, precision_score, recall_score, classification_report

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, callbacks
from tensorflow.keras.models import Model
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, MaxPooling2D, AveragePooling2D, GlobalAveragePooling2D, Concatenate, Dense, Flatten, Add, Dropout
from tensorflow.keras.optimizers import Adam
import warnings
warnings.filterwarnings('ignore')

The Chest X-ray Dataset

In [2]:
#Defining the Directory Paths to the dataset
chest_xray_dir = "C:/Users/Zhong Da Goh/Desktop/CM3070/TB_Chest_Radiography_Database"
normal_xray_dir = os.path.join(chest_xray_dir,'Normal')
tb_xray_dir = os.path.join(chest_xray_dir,'Tuberculosis')

#Listing the PNG files 
normal_images = [file for file in os.listdir(normal_xray_dir)]
tb_images = [file for file in os.listdir(tb_xray_dir)]

# Count the number of image files in each directory
num_normal_images = len(normal_images)
num_tb_images = len(tb_images)

print(f"Normal Xray Set has: {num_normal_images} images")
print(f"Tuberculosis Xray Set has: {num_tb_images} images")
Normal Xray Set has: 3500 images
Tuberculosis Xray Set has: 700 images
In [3]:
img_size = (244, 244)  # Target size to resize images
num_images = 5  # Number of images to randomly select and display

# Select random images from the directories
random_normal_images = random.sample(normal_images, len(normal_images))  # Randomly select images from 'Normal' directory
random_tb_images = random.sample(tb_images, len(tb_images))              # Randomly select images from 'Tuberculosis' directory

# Load and resizes images function
def load_and_resize_image(directory, file, size):
    img_path = os.path.join(directory, file)    # Construct the full path to the image file
    img = cv2.imread(img_path)                  # Read the image using OpenCV
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # Convert the image from BGR (OpenCV default) to RGB format
    img = cv2.resize(img, size)                 # Resize the image to the specified size
    img = img / 255.0                           # Normalize pixel values to the range [0, 1]
    return img                                  # Returns the processed image

# Load images from both directories and resize them using the defined function
normal_imgs = [load_and_resize_image(normal_xray_dir, file, img_size) for file in random_normal_images]  # Load and resize images from 'Normal' directory
tb_imgs = [load_and_resize_image(tb_xray_dir, file, img_size) for file in random_tb_images]  # Load and resize images from 'Tuberculosis' directory
In [4]:
# Create a 2x5 grid for displaying images
fig, axes = plt.subplots(2, num_images, figsize=(15, 6)) 

# Display images from the 'Normal' category
for i in range(num_images):
    axes[0, i].imshow(normal_imgs[i])  # Display the i-th image from the 'Normal' images list
    axes[0, i].axis('off')             # Hide the axis for a cleaner look
    axes[0, i].set_title('Normal')     # Set the title for each subplot to 'Normal'

# Display images from the 'Tuberculosis' category
for i in range(num_images):
    axes[1, i].imshow(tb_imgs[i])         # Display the i-th image from the 'Tuberculosis' images list
    axes[1, i].axis('off')                # Hide the axis for a cleaner look
    axes[1, i].set_title('Tuberculosis')  # Set the title for each subplot to 'Tuberculosis'

plt.tight_layout()  # Adjust subplot parameters to give specified padding
plt.show()  # Display the plot with all images
No description has been provided for this image

Stratified Train-Test Split (Evaluation Protocol)

In [5]:
# Combine the lists of all images and create labels
combined_images = normal_images + tb_images
labels = [0] * len(normal_images) + [1] * len(tb_images)  # 0 for Normal, 1 for Tuberculosis

# Perform stratified train-test split
train_images, test_images, train_labels, test_labels = train_test_split(combined_images, labels, 
                                                                        test_size=0.2,     #20% of the data will be used as the test set
                                                                        stratify=labels, 
                                                                        random_state=50)   #For reproducibility

# Print the sizes of the training and test sets
print(f"Training set: {len(train_images)} images")
print(f"Test set: {len(test_images)} images")
Training set: 3360 images
Test set: 840 images
In [6]:
# Load and preprocess training images
X_train = np.array([load_and_resize_image(normal_xray_dir if label == 0 else tb_xray_dir, img, img_size) 
                    for img, label in zip(train_images, train_labels)])
y_train = np.array(train_labels)

# Load and preprocess testing images
X_test = np.array([load_and_resize_image(normal_xray_dir if label == 0 else tb_xray_dir, img, img_size) 
                   for img, label in zip(test_images, test_labels)])
y_test = np.array(test_labels)

# Print the shapes of the training and test sets
print(f"X_train shape: {X_train.shape}")
print(f"y_train shape: {y_train.shape}")
print(f"X_test shape: {X_test.shape}")
print(f"y_test shape: {y_test.shape}")
X_train shape: (3360, 244, 244, 3)
y_train shape: (3360,)
X_test shape: (840, 244, 244, 3)
y_test shape: (840,)

Augmenting the training data

In [7]:
img_width, img_height = 244, 244  # Image dimensions
batch_size = 32
epochs = 10
random_seed = 50

# Create ImageDataGenerator instances
train_datagen = ImageDataGenerator(
    rescale=1./255,                # Normalize pixel values to the range [0, 1]
    rotation_range=10,             # Rotate by a certain degree range to simulate different angles at which X-rays might be taken
    brightness_range=[0.8, 1.2],   # Adjust brightness 
    zoom_range=0.1,                # Apply random zoom transformations
    width_shift_range=0.1,         # Apply random horizontal shifts
    height_shift_range=0.1,        # Apply random vertical shifts
    horizontal_flip=True,          # Flip images horizontally
    validation_split=0.2  # Splitting training data into training and validation sets
)

# Generate batches of augmented data
train_dataset = train_datagen.flow_from_directory(
    chest_xray_dir,
    target_size=(img_width, img_height),    # Resize images to a specific size
    batch_size=batch_size,                  # Batch size for training
    class_mode='binary',                    # Binary classification (two classes)
    subset='training',                      # Specify training data
    shuffle=True,                           # Shuffle the data
    seed=random_seed                        # Random seed for reproducibility
)

validation_dataset = train_datagen.flow_from_directory(
    chest_xray_dir,
    target_size=(img_width, img_height),   # Resize images to a specific size
    batch_size=batch_size,                 # Batch size for validation
    class_mode='binary',                   # Binary classification (two classes)
    subset='validation',                   # Specify validation data
    shuffle=False,                         # Do not shuffle the data for validation
    seed=random_seed                       # Random seed for reproducibility
)
Found 3360 images belonging to 2 classes.
Found 840 images belonging to 2 classes.

Visualising the augmented training data

In [12]:
# Get a batch of images and labels from the training dataset
images, labels = next(train_dataset) 

# Define the number of images to display
num_images_to_display = 10  

plt.figure(figsize=(15, 10))

# Plot images
for i in range(num_images_to_display):
    plt.subplot(1, num_images_to_display, i + 1)  # Create subplots
    plt.imshow(images[i], cmap='gray')  # Display the image in grayscale 
    plt.title(f"Label: {'Normal' if labels[i] == 0 else 'Tuberculosis'}")  # Display the label
    plt.axis('off')  # Hide axis

plt.tight_layout()
plt.show()
No description has been provided for this image

Developing a Convolutional Neural Network (CNN) Model

image.png

Callbacks for training models

In [16]:
# Define an EarlyStopping callback
early_stopping = callbacks.EarlyStopping(
    monitor='val_loss',            # Metric to monitor for early stopping (validation loss)
    patience=5,                    # Number of epochs with no improvement before stopping
    min_delta=1e-7,                # Minimum change in monitored metric to be considered an improvement
    verbose=1,                     # Verbosity level (1 for updates)
    restore_best_weights=True,     # Restore model weights to the best state when stopped
)

# Define a ReduceLROnPlateau callback
reduceLR = callbacks.ReduceLROnPlateau(
    monitor='val_loss',            # Metric to monitor for learning rate reduction (validation loss)
    factor=0.5,                    # Factor by which the learning rate will be reduced (e.g., 0.2 means lr *= 0.2)
    patience=2,                    # Number of epochs with no improvement before reducing the learning rate
    min_delta=1e-6,                # Minimum change in monitored metric to trigger a reduction
    cooldown=0,                    # Number of epochs to wait after a reduction before resuming normal operation
    verbose=1                      # Verbosity level (1 for updates)
)
In [17]:
def CNNModel():
  # Input layer
  inputs = Input(shape=(img_height, img_width, 3))

  # Convolutional layers
  x = Conv2D(32, (3, 3), activation='relu')(inputs)
  x = MaxPooling2D((2, 2))(x)
  x = Conv2D(64, (3, 3), activation='relu')(x)
  x = MaxPooling2D((2, 2))(x)

  # Flatten layer
  x = Flatten()(x)

  # Fully connected layers
  x = Dense(64, activation='relu')(x)
  output = Dense(1, activation='sigmoid')(x) 

  # Create model
  model = Model(inputs=[inputs], outputs=output, name='CNN_Model')

  return model

# Clear the Keras session to release resources
keras.backend.clear_session()

# Create a CNN model using the 'CNNModel' function
CNNModel = CNNModel()

# Compile the CNN model with specified loss, optimizer, and metrics
CNNModel.compile(
    loss='binary_crossentropy',  # Binary cross-entropy loss for binary classification
    optimizer=keras.optimizers.Adam(),  # Adam optimizer with a custom learning rate
    metrics=['binary_accuracy']  # Metric to monitor during training (binary accuracy)
)

# Display a summary of the model architecture
CNNModel.summary()
WARNING:tensorflow:From C:\Users\Zhong Da Goh\AppData\Local\Programs\Python\Python310\lib\site-packages\keras\src\backend\common\global_state.py:74: The name tf.reset_default_graph is deprecated. Please use tf.compat.v1.reset_default_graph instead.

Model: "CNN_Model"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃ Layer (type)                         ┃ Output Shape                ┃         Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer (InputLayer)             │ (None, 244, 244, 3)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d (Conv2D)                      │ (None, 242, 242, 32)        │             896 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d (MaxPooling2D)         │ (None, 121, 121, 32)        │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_1 (Conv2D)                    │ (None, 119, 119, 64)        │          18,496 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d_1 (MaxPooling2D)       │ (None, 59, 59, 64)          │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ flatten (Flatten)                    │ (None, 222784)              │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense (Dense)                        │ (None, 64)                  │      14,258,240 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_1 (Dense)                      │ (None, 1)                   │              65 │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
 Total params: 14,277,697 (54.47 MB)
 Trainable params: 14,277,697 (54.47 MB)
 Non-trainable params: 0 (0.00 B)
In [18]:
# Train the model
CNNModel_hist = CNNModel.fit(train_dataset,
          validation_data=validation_dataset,
          epochs=10,
          batch_size=batch_size,
          callbacks=[early_stopping,reduceLR]  #  Callbacks for early stopping and learning rate reduction
)
Epoch 1/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 87s 790ms/step - binary_accuracy: 0.7432 - loss: 0.9890 - val_binary_accuracy: 0.8595 - val_loss: 0.4077 - learning_rate: 0.0010
Epoch 2/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 88s 806ms/step - binary_accuracy: 0.9310 - loss: 0.2338 - val_binary_accuracy: 0.8738 - val_loss: 0.2489 - learning_rate: 0.0010
Epoch 3/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 88s 806ms/step - binary_accuracy: 0.9372 - loss: 0.1767 - val_binary_accuracy: 0.9167 - val_loss: 0.1808 - learning_rate: 0.0010
Epoch 4/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 88s 802ms/step - binary_accuracy: 0.9460 - loss: 0.1554 - val_binary_accuracy: 0.9083 - val_loss: 0.1742 - learning_rate: 0.0010
Epoch 5/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 87s 797ms/step - binary_accuracy: 0.9505 - loss: 0.1404 - val_binary_accuracy: 0.9369 - val_loss: 0.1592 - learning_rate: 0.0010
Epoch 6/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 88s 802ms/step - binary_accuracy: 0.9561 - loss: 0.1204 - val_binary_accuracy: 0.9452 - val_loss: 0.1522 - learning_rate: 0.0010
Epoch 7/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 87s 799ms/step - binary_accuracy: 0.9550 - loss: 0.1223 - val_binary_accuracy: 0.9476 - val_loss: 0.1475 - learning_rate: 0.0010
Epoch 8/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 88s 807ms/step - binary_accuracy: 0.9536 - loss: 0.1429 - val_binary_accuracy: 0.9298 - val_loss: 0.2048 - learning_rate: 0.0010
Epoch 9/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 0s 675ms/step - binary_accuracy: 0.9388 - loss: 0.1843
Epoch 9: ReduceLROnPlateau reducing learning rate to 0.0005000000237487257.
105/105 ━━━━━━━━━━━━━━━━━━━━ 87s 799ms/step - binary_accuracy: 0.9389 - loss: 0.1840 - val_binary_accuracy: 0.8905 - val_loss: 0.2190 - learning_rate: 0.0010
Epoch 10/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 87s 801ms/step - binary_accuracy: 0.9670 - loss: 0.0980 - val_binary_accuracy: 0.9440 - val_loss: 0.1412 - learning_rate: 5.0000e-04
Restoring model weights from the end of the best epoch: 10.

Visualisation of the CNN Model's training

In [19]:
# Plotting the results of model training
plt.figure(figsize=(12, 6))

# Plot accuracy curve
plt.subplot(1, 2, 1)
plt.plot(CNNModel_hist.history['binary_accuracy'], label='Training Accuracy')
plt.plot(CNNModel_hist.history['val_binary_accuracy'], label='Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Training and Validation Accuracy (CNN)')
plt.legend()

# Plot loss curve
plt.subplot(1, 2, 2)
plt.plot(CNNModel_hist.history['loss'], label='Training Loss')
plt.plot(CNNModel_hist.history['val_loss'], label='Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training and Validation Loss (CNN)')
plt.legend()

plt.tight_layout()
plt.show()
No description has been provided for this image

Evaluation of the CNN Model

In [20]:
# Evaluate the model on the validation set
val_loss, val_acc = CNNModel.evaluate(validation_dataset, verbose=2)
print(f'Validation accuracy: {val_acc}, Validation loss: {val_loss}')

# Evaluate the model on the test set
test_loss, test_acc = CNNModel.evaluate(X_test, y_test, verbose=2)
print(f'Test accuracy: {test_acc}, Test loss: {test_loss}')
27/27 - 13s - 479ms/step - binary_accuracy: 0.9524 - loss: 0.1215
Validation accuracy: 0.9523809552192688, Validation loss: 0.12148359417915344
27/27 - 2s - 84ms/step - binary_accuracy: 0.9750 - loss: 0.0674
Test accuracy: 0.9750000238418579, Test loss: 0.06739521771669388
In [21]:
# Evaluate the model on the test set and obtain predictions
test_predictionsCNN = CNNModel.predict(X_test, batch_size=batch_size, verbose=1)

# Convert probability scores to binary predictions (0 or 1)
testProbability_predictionsCNN = (test_predictionsCNN > 0.5).astype(int)

# Generate a classification report for precision, recall, and F1-score
testClassificationReport_CNN = classification_report(y_test, testProbability_predictionsCNN, output_dict=True)

# Print the test accuracy, precision, recall, and F1-score
print('Test Accuracy:', testClassificationReport_CNN['accuracy'])
print('Test Precision (Overall):', testClassificationReport_CNN['macro avg']['precision'])
print('Test Recall (Overall):', testClassificationReport_CNN['macro avg']['recall'])
print('Test F1 Score (Overall):', testClassificationReport_CNN['macro avg']['f1-score'])
27/27 ━━━━━━━━━━━━━━━━━━━━ 2s 84ms/step
Test Accuracy: 0.975
Test Precision (Overall): 0.9786202673742219
Test Recall (Overall): 0.9307142857142857
Test F1 Score (Overall): 0.9526660244558873
In [22]:
# Calculate the confusion matrix between true labels and predicted labels
confusionMatrix_CNN = metrics.confusion_matrix(y_test, testProbability_predictionsCNN)

# Create a heatmap of the confusion matrix with annotations
sns.heatmap(confusionMatrix_CNN, annot=True, fmt="d", xticklabels=['NORMAL', 'TUBERCULOSIS'], yticklabels=['NORMAL', 'TUBERCULOSIS'])

# Set the x-axis label
plt.xlabel("Predicted Label", fontsize=12)

# Set the y-axis label
plt.ylabel("True Label", fontsize=12)

# Display the heatmap
plt.show()
No description has been provided for this image
In [37]:
# Select a random sample of images to display
num_images_to_display = 10  
indices = np.random.choice(len(X_test), num_images_to_display, replace=False)  # Randomly select indices of images from the test set

plt.figure(figsize=(15, 10))

# Loop through the randomly selected images and display them
for i, idx in enumerate(indices):
    plt.subplot(2, 5, i + 1)                               # Create a subplot in a 2x5 grid
    plt.imshow(X_test[idx], cmap='gray')                   # Display the image in grayscale
    true_label = y_test[idx]                               # Get the true label of the current image
    predicted_label = testProbability_predictionsCNN[idx]  # Get the predicted label from the model
    
    # Set the title color: green if the prediction is correct, red if incorrect
    color = 'green' if true_label == predicted_label else 'red'
    
    # Display the true and predicted labels with color coding
    plt.title(f"True: {true_label} \nPred: {predicted_label}", color=color)
    plt.axis('off')

plt.tight_layout()
plt.show() 
No description has been provided for this image

ResNet50 Model

In [49]:
def convolutionalBlock(x, filters, stage, block, stride=2):
    """
    Implementation of the convolutional block
    Arguments:
    x -- input tensor of shape (m, n_H_prev, n_W_prev, n_C_prev)
    filters -- python list of integers, defining the number of filters in the CONV layers of the main path
    stage -- integer, used to name the layers, depending on their position in the network
    block -- string/character, used to name the layers, depending on their position in the network
    stride -- Integer, specifying the stride to be used
    Returns:
    x -- output of the convolutional block, tensor of shape (n_H, n_W, n_C)
    """

    convolutionalName = 'convolutionalStage' + str(stage) + 'Block' + str(block) 
    batchNormName = 'blockStage' + str(stage) + 'Block' + str(block) 

    f1, f2, f3 = filters

    inputCopy = x

    # First component of main path
    x = Conv2D(f1, (1, 1), strides=(stride, stride), name=convolutionalName + '2a')(x)
    x = BatchNormalization(axis=3, name=batchNormName + '2a')(x)
    x = Activation('relu')(x)

    # Second component of main path
    x = Conv2D(f2, (3, 3), strides=(1, 1), padding='same', name=convolutionalName + '2b')(x)
    x = BatchNormalization(axis=3, name=batchNormName + '2b')(x)
    x = Activation('relu')(x)

    # Third component of main path
    x = Conv2D(f3, (1, 1), strides=(1, 1), name=convolutionalName + '2c')(x)
    x = BatchNormalization(axis=3, name=batchNormName + '2c')(x)

    # Shortcut path
    # Conv2D to make shape the same size
    inputCopy = Conv2D(f3, (1, 1), strides=(stride, stride), name=convolutionalName + '1')(inputCopy)
    inputCopy = BatchNormalization(axis=3, name=batchNormName + '1')(inputCopy)

    # Final step: Add shortcut value to main path, and pass it through a RELU activation
    x = Add()([x, inputCopy])
    x = Activation('relu')(x)

    return x

def identityBlock(x, filters, stage, block):
    """
    Implementation of the identity block
    Arguments:
    x -- input tensor of shape (m, n_H_prev, n_W_prev, n_C_prev)
    filters -- python list of integers, defining the number of filters in the CONV layers of the main path
    stage -- integer, used to name the layers, depending on their position in the network
    block -- string/character, used to name the layers, depending on their position in the network
    Returns:
    x -- output of the identity block, tensor of shape (n_H, n_W, n_C)
    """

    convolutionalName = 'convolutionalStage' + str(stage) + 'Block' + str(block)
    batchNormName = 'blockStage' + str(stage) + 'Block' + str(block)

    f1, f2, f3 = filters

    inputCopy = x

    # First component of main path
    x = Conv2D(filters=f1, kernel_size=(1, 1), strides=(1, 1), padding='valid', name=convolutionalName + '2a')(x)
    x = BatchNormalization(axis=3, name=batchNormName + '2a')(x)
    x = Activation('relu')(x)

    # Second component of main path
    x = Conv2D(filters=f2, kernel_size=(3, 3), strides=(1, 1), padding='same', name=convolutionalName + '2b')(x)
    x = BatchNormalization(axis=3, name=batchNormName + '2b')(x)
    x = Activation('relu')(x)

    # Third component of main path
    x = Conv2D(filters=f3, kernel_size=(1, 1), strides=(1, 1), padding='valid', name=convolutionalName + '2c')(x)
    x = BatchNormalization(axis=3, name=batchNormName + '2c')(x)

    # Final step: Add shortcut value to main path, and pass it through a RELU activation
    x = Add()([x, inputCopy])
    x = Activation('relu')(x)

    return x

def resnet50Model():
  inputs = Input(shape=(img_height, img_width, 3), name='input')

  # conv1 - Stage 1, Block 1
  x = Conv2D(filters=64, kernel_size=7, padding='same', strides=2, name="res1")(inputs)
  x = BatchNormalization(axis=3, name="bn1")(x)
  x = Activation('relu')(x)
  x = MaxPooling2D(pool_size=3, padding='same', strides=2)(x)

  # conv2 - Stage 2, Block 2-4
  x = convolutionalBlock(x, filters=[64, 64, 256], stage=2, block=2, stride=1)
  x = identityBlock(x, filters=[64, 64, 256], stage=2, block=3)
  x = identityBlock(x, filters=[64, 64, 256], stage=2, block=4)

  # conv3 - Stage 3, Block 5-8
  x = convolutionalBlock(x, filters=[128, 128, 512], stage=3, block=5, stride=2)
  x = identityBlock(x, filters=[128, 128, 512], stage=3, block=6)
  x = identityBlock(x, filters=[128, 128, 512], stage=3, block=7)
  x = identityBlock(x, filters=[128, 128, 512], stage=3, block=8)

  # conv4 - Stage 4, Block 9-14
  x = convolutionalBlock(x, filters=[256, 256, 1024], stage=4, block=9, stride=2)
  x = identityBlock(x, filters=[256, 256, 1024], stage=4, block=10)
  x = identityBlock(x, filters=[256, 256, 1024], stage=4, block=11)
  x = identityBlock(x, filters=[256, 256, 1024], stage=4, block=12)
  x = identityBlock(x, filters=[256, 256, 1024], stage=4, block=13)
  x = identityBlock(x, filters=[256, 256, 1024], stage=4, block=14)

  # conv5 - Stage 5, Block 15-17
  x = convolutionalBlock(x, filters=[512, 512, 2048], stage=5, block=15, stride=2)
  x = identityBlock(x, filters=[512, 512, 2048], stage=5, block=16)
  x = identityBlock(x, filters=[512, 512, 2048], stage=5, block=17)

  # Block 18
  x = AveragePooling2D(pool_size=7, padding='same')(x)
  x = Dropout(0.1)(x)
  x = Flatten()(x)

  # Add fully connected layers
  x = Dense(512, activation='relu')(x)
  x = Dropout(0.1)(x) 
  x = Dense(256, activation='relu')(x)

  # Last layer
  output = Dense(1, activation='sigmoid')(x)

  # Create the model with defined inputs and outputs
  model = keras.Model(inputs=[inputs], outputs=output, name='ResNet50_Model')

  return model
In [50]:
# Clear the Keras session to release resources
keras.backend.clear_session()

# Create a CNN model using the defined 'baseline_model' function
resNetModel = resnet50Model()

# Compile the CNN model with specified loss, optimizer, and metrics
resNetModel.compile(
    loss='binary_crossentropy',  # Binary cross-entropy loss for binary classification
    optimizer=keras.optimizers.Adam(),  # Adam optimizer with a custom learning rate
    metrics=['binary_accuracy']  # Metric to monitor during training (binary accuracy)
)

# Summary of the ResNet50 Model
resNetModel.summary()
Model: "ResNet50_Model"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Layer (type)                  ┃ Output Shape              ┃         Param # ┃ Connected to               ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ input (InputLayer)            │ (None, 244, 244, 3)       │               0 │ -                          │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ res1 (Conv2D)                 │ (None, 122, 122, 64)      │           9,472 │ input[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ bn1 (BatchNormalization)      │ (None, 122, 122, 64)      │             256 │ res1[0][0]                 │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation (Activation)       │ (None, 122, 122, 64)      │               0 │ bn1[0][0]                  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ max_pooling2d (MaxPooling2D)  │ (None, 61, 61, 64)        │               0 │ activation[0][0]           │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block22a   │ (None, 61, 61, 64)        │           4,160 │ max_pooling2d[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block22a           │ (None, 61, 61, 64)        │             256 │ convolutionalStage2Block2… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_1 (Activation)     │ (None, 61, 61, 64)        │               0 │ blockStage2Block22a[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block22b   │ (None, 61, 61, 64)        │          36,928 │ activation_1[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block22b           │ (None, 61, 61, 64)        │             256 │ convolutionalStage2Block2… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_2 (Activation)     │ (None, 61, 61, 64)        │               0 │ blockStage2Block22b[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block22c   │ (None, 61, 61, 256)       │          16,640 │ activation_2[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block21    │ (None, 61, 61, 256)       │          16,640 │ max_pooling2d[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block22c           │ (None, 61, 61, 256)       │           1,024 │ convolutionalStage2Block2… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block21            │ (None, 61, 61, 256)       │           1,024 │ convolutionalStage2Block2… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add (Add)                     │ (None, 61, 61, 256)       │               0 │ blockStage2Block22c[0][0], │
│                               │                           │                 │ blockStage2Block21[0][0]   │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_3 (Activation)     │ (None, 61, 61, 256)       │               0 │ add[0][0]                  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block32a   │ (None, 61, 61, 64)        │          16,448 │ activation_3[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block32a           │ (None, 61, 61, 64)        │             256 │ convolutionalStage2Block3… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_4 (Activation)     │ (None, 61, 61, 64)        │               0 │ blockStage2Block32a[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block32b   │ (None, 61, 61, 64)        │          36,928 │ activation_4[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block32b           │ (None, 61, 61, 64)        │             256 │ convolutionalStage2Block3… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_5 (Activation)     │ (None, 61, 61, 64)        │               0 │ blockStage2Block32b[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block32c   │ (None, 61, 61, 256)       │          16,640 │ activation_5[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block32c           │ (None, 61, 61, 256)       │           1,024 │ convolutionalStage2Block3… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_1 (Add)                   │ (None, 61, 61, 256)       │               0 │ blockStage2Block32c[0][0], │
│                               │                           │                 │ activation_3[0][0]         │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_6 (Activation)     │ (None, 61, 61, 256)       │               0 │ add_1[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block42a   │ (None, 61, 61, 64)        │          16,448 │ activation_6[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block42a           │ (None, 61, 61, 64)        │             256 │ convolutionalStage2Block4… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_7 (Activation)     │ (None, 61, 61, 64)        │               0 │ blockStage2Block42a[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block42b   │ (None, 61, 61, 64)        │          36,928 │ activation_7[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block42b           │ (None, 61, 61, 64)        │             256 │ convolutionalStage2Block4… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_8 (Activation)     │ (None, 61, 61, 64)        │               0 │ blockStage2Block42b[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage2Block42c   │ (None, 61, 61, 256)       │          16,640 │ activation_8[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage2Block42c           │ (None, 61, 61, 256)       │           1,024 │ convolutionalStage2Block4… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_2 (Add)                   │ (None, 61, 61, 256)       │               0 │ blockStage2Block42c[0][0], │
│                               │                           │                 │ activation_6[0][0]         │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_9 (Activation)     │ (None, 61, 61, 256)       │               0 │ add_2[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block52a   │ (None, 31, 31, 128)       │          32,896 │ activation_9[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block52a           │ (None, 31, 31, 128)       │             512 │ convolutionalStage3Block5… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_10 (Activation)    │ (None, 31, 31, 128)       │               0 │ blockStage3Block52a[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block52b   │ (None, 31, 31, 128)       │         147,584 │ activation_10[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block52b           │ (None, 31, 31, 128)       │             512 │ convolutionalStage3Block5… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_11 (Activation)    │ (None, 31, 31, 128)       │               0 │ blockStage3Block52b[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block52c   │ (None, 31, 31, 512)       │          66,048 │ activation_11[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block51    │ (None, 31, 31, 512)       │         131,584 │ activation_9[0][0]         │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block52c           │ (None, 31, 31, 512)       │           2,048 │ convolutionalStage3Block5… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block51            │ (None, 31, 31, 512)       │           2,048 │ convolutionalStage3Block5… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_3 (Add)                   │ (None, 31, 31, 512)       │               0 │ blockStage3Block52c[0][0], │
│                               │                           │                 │ blockStage3Block51[0][0]   │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_12 (Activation)    │ (None, 31, 31, 512)       │               0 │ add_3[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block62a   │ (None, 31, 31, 128)       │          65,664 │ activation_12[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block62a           │ (None, 31, 31, 128)       │             512 │ convolutionalStage3Block6… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_13 (Activation)    │ (None, 31, 31, 128)       │               0 │ blockStage3Block62a[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block62b   │ (None, 31, 31, 128)       │         147,584 │ activation_13[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block62b           │ (None, 31, 31, 128)       │             512 │ convolutionalStage3Block6… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_14 (Activation)    │ (None, 31, 31, 128)       │               0 │ blockStage3Block62b[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block62c   │ (None, 31, 31, 512)       │          66,048 │ activation_14[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block62c           │ (None, 31, 31, 512)       │           2,048 │ convolutionalStage3Block6… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_4 (Add)                   │ (None, 31, 31, 512)       │               0 │ blockStage3Block62c[0][0], │
│                               │                           │                 │ activation_12[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_15 (Activation)    │ (None, 31, 31, 512)       │               0 │ add_4[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block72a   │ (None, 31, 31, 128)       │          65,664 │ activation_15[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block72a           │ (None, 31, 31, 128)       │             512 │ convolutionalStage3Block7… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_16 (Activation)    │ (None, 31, 31, 128)       │               0 │ blockStage3Block72a[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block72b   │ (None, 31, 31, 128)       │         147,584 │ activation_16[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block72b           │ (None, 31, 31, 128)       │             512 │ convolutionalStage3Block7… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_17 (Activation)    │ (None, 31, 31, 128)       │               0 │ blockStage3Block72b[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block72c   │ (None, 31, 31, 512)       │          66,048 │ activation_17[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block72c           │ (None, 31, 31, 512)       │           2,048 │ convolutionalStage3Block7… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_5 (Add)                   │ (None, 31, 31, 512)       │               0 │ blockStage3Block72c[0][0], │
│                               │                           │                 │ activation_15[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_18 (Activation)    │ (None, 31, 31, 512)       │               0 │ add_5[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block82a   │ (None, 31, 31, 128)       │          65,664 │ activation_18[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block82a           │ (None, 31, 31, 128)       │             512 │ convolutionalStage3Block8… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_19 (Activation)    │ (None, 31, 31, 128)       │               0 │ blockStage3Block82a[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block82b   │ (None, 31, 31, 128)       │         147,584 │ activation_19[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block82b           │ (None, 31, 31, 128)       │             512 │ convolutionalStage3Block8… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_20 (Activation)    │ (None, 31, 31, 128)       │               0 │ blockStage3Block82b[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage3Block82c   │ (None, 31, 31, 512)       │          66,048 │ activation_20[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage3Block82c           │ (None, 31, 31, 512)       │           2,048 │ convolutionalStage3Block8… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_6 (Add)                   │ (None, 31, 31, 512)       │               0 │ blockStage3Block82c[0][0], │
│                               │                           │                 │ activation_18[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_21 (Activation)    │ (None, 31, 31, 512)       │               0 │ add_6[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block92a   │ (None, 16, 16, 256)       │         131,328 │ activation_21[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block92a           │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block9… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_22 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block92a[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block92b   │ (None, 16, 16, 256)       │         590,080 │ activation_22[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block92b           │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block9… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_23 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block92b[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block92c   │ (None, 16, 16, 1024)      │         263,168 │ activation_23[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block91    │ (None, 16, 16, 1024)      │         525,312 │ activation_21[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block92c           │ (None, 16, 16, 1024)      │           4,096 │ convolutionalStage4Block9… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block91            │ (None, 16, 16, 1024)      │           4,096 │ convolutionalStage4Block9… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_7 (Add)                   │ (None, 16, 16, 1024)      │               0 │ blockStage4Block92c[0][0], │
│                               │                           │                 │ blockStage4Block91[0][0]   │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_24 (Activation)    │ (None, 16, 16, 1024)      │               0 │ add_7[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block102a  │ (None, 16, 16, 256)       │         262,400 │ activation_24[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block102a          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_25 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block102a[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block102b  │ (None, 16, 16, 256)       │         590,080 │ activation_25[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block102b          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_26 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block102b[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block102c  │ (None, 16, 16, 1024)      │         263,168 │ activation_26[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block102c          │ (None, 16, 16, 1024)      │           4,096 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_8 (Add)                   │ (None, 16, 16, 1024)      │               0 │ blockStage4Block102c[0][0… │
│                               │                           │                 │ activation_24[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_27 (Activation)    │ (None, 16, 16, 1024)      │               0 │ add_8[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block112a  │ (None, 16, 16, 256)       │         262,400 │ activation_27[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block112a          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_28 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block112a[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block112b  │ (None, 16, 16, 256)       │         590,080 │ activation_28[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block112b          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_29 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block112b[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block112c  │ (None, 16, 16, 1024)      │         263,168 │ activation_29[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block112c          │ (None, 16, 16, 1024)      │           4,096 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_9 (Add)                   │ (None, 16, 16, 1024)      │               0 │ blockStage4Block112c[0][0… │
│                               │                           │                 │ activation_27[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_30 (Activation)    │ (None, 16, 16, 1024)      │               0 │ add_9[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block122a  │ (None, 16, 16, 256)       │         262,400 │ activation_30[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block122a          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_31 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block122a[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block122b  │ (None, 16, 16, 256)       │         590,080 │ activation_31[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block122b          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_32 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block122b[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block122c  │ (None, 16, 16, 1024)      │         263,168 │ activation_32[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block122c          │ (None, 16, 16, 1024)      │           4,096 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_10 (Add)                  │ (None, 16, 16, 1024)      │               0 │ blockStage4Block122c[0][0… │
│                               │                           │                 │ activation_30[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_33 (Activation)    │ (None, 16, 16, 1024)      │               0 │ add_10[0][0]               │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block132a  │ (None, 16, 16, 256)       │         262,400 │ activation_33[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block132a          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_34 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block132a[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block132b  │ (None, 16, 16, 256)       │         590,080 │ activation_34[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block132b          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_35 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block132b[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block132c  │ (None, 16, 16, 1024)      │         263,168 │ activation_35[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block132c          │ (None, 16, 16, 1024)      │           4,096 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_11 (Add)                  │ (None, 16, 16, 1024)      │               0 │ blockStage4Block132c[0][0… │
│                               │                           │                 │ activation_33[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_36 (Activation)    │ (None, 16, 16, 1024)      │               0 │ add_11[0][0]               │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block142a  │ (None, 16, 16, 256)       │         262,400 │ activation_36[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block142a          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_37 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block142a[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block142b  │ (None, 16, 16, 256)       │         590,080 │ activation_37[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block142b          │ (None, 16, 16, 256)       │           1,024 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_38 (Activation)    │ (None, 16, 16, 256)       │               0 │ blockStage4Block142b[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage4Block142c  │ (None, 16, 16, 1024)      │         263,168 │ activation_38[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage4Block142c          │ (None, 16, 16, 1024)      │           4,096 │ convolutionalStage4Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_12 (Add)                  │ (None, 16, 16, 1024)      │               0 │ blockStage4Block142c[0][0… │
│                               │                           │                 │ activation_36[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_39 (Activation)    │ (None, 16, 16, 1024)      │               0 │ add_12[0][0]               │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block152a  │ (None, 8, 8, 512)         │         524,800 │ activation_39[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block152a          │ (None, 8, 8, 512)         │           2,048 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_40 (Activation)    │ (None, 8, 8, 512)         │               0 │ blockStage5Block152a[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block152b  │ (None, 8, 8, 512)         │       2,359,808 │ activation_40[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block152b          │ (None, 8, 8, 512)         │           2,048 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_41 (Activation)    │ (None, 8, 8, 512)         │               0 │ blockStage5Block152b[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block152c  │ (None, 8, 8, 2048)        │       1,050,624 │ activation_41[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block151   │ (None, 8, 8, 2048)        │       2,099,200 │ activation_39[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block152c          │ (None, 8, 8, 2048)        │           8,192 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block151           │ (None, 8, 8, 2048)        │           8,192 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_13 (Add)                  │ (None, 8, 8, 2048)        │               0 │ blockStage5Block152c[0][0… │
│                               │                           │                 │ blockStage5Block151[0][0]  │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_42 (Activation)    │ (None, 8, 8, 2048)        │               0 │ add_13[0][0]               │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block162a  │ (None, 8, 8, 512)         │       1,049,088 │ activation_42[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block162a          │ (None, 8, 8, 512)         │           2,048 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_43 (Activation)    │ (None, 8, 8, 512)         │               0 │ blockStage5Block162a[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block162b  │ (None, 8, 8, 512)         │       2,359,808 │ activation_43[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block162b          │ (None, 8, 8, 512)         │           2,048 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_44 (Activation)    │ (None, 8, 8, 512)         │               0 │ blockStage5Block162b[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block162c  │ (None, 8, 8, 2048)        │       1,050,624 │ activation_44[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block162c          │ (None, 8, 8, 2048)        │           8,192 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_14 (Add)                  │ (None, 8, 8, 2048)        │               0 │ blockStage5Block162c[0][0… │
│                               │                           │                 │ activation_42[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_45 (Activation)    │ (None, 8, 8, 2048)        │               0 │ add_14[0][0]               │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block172a  │ (None, 8, 8, 512)         │       1,049,088 │ activation_45[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block172a          │ (None, 8, 8, 512)         │           2,048 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_46 (Activation)    │ (None, 8, 8, 512)         │               0 │ blockStage5Block172a[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block172b  │ (None, 8, 8, 512)         │       2,359,808 │ activation_46[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block172b          │ (None, 8, 8, 512)         │           2,048 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_47 (Activation)    │ (None, 8, 8, 512)         │               0 │ blockStage5Block172b[0][0] │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ convolutionalStage5Block172c  │ (None, 8, 8, 2048)        │       1,050,624 │ activation_47[0][0]        │
│ (Conv2D)                      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ blockStage5Block172c          │ (None, 8, 8, 2048)        │           8,192 │ convolutionalStage5Block1… │
│ (BatchNormalization)          │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ add_15 (Add)                  │ (None, 8, 8, 2048)        │               0 │ blockStage5Block172c[0][0… │
│                               │                           │                 │ activation_45[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ activation_48 (Activation)    │ (None, 8, 8, 2048)        │               0 │ add_15[0][0]               │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ average_pooling2d             │ (None, 2, 2, 2048)        │               0 │ activation_48[0][0]        │
│ (AveragePooling2D)            │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ dropout (Dropout)             │ (None, 2, 2, 2048)        │               0 │ average_pooling2d[0][0]    │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ flatten (Flatten)             │ (None, 8192)              │               0 │ dropout[0][0]              │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ dense (Dense)                 │ (None, 512)               │       4,194,816 │ flatten[0][0]              │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ dropout_1 (Dropout)           │ (None, 512)               │               0 │ dense[0][0]                │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ dense_1 (Dense)               │ (None, 256)               │         131,328 │ dropout_1[0][0]            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ dense_2 (Dense)               │ (None, 1)                 │             257 │ dense_1[0][0]              │
└───────────────────────────────┴───────────────────────────┴─────────────────┴────────────────────────────┘
 Total params: 27,914,113 (106.48 MB)
 Trainable params: 27,860,993 (106.28 MB)
 Non-trainable params: 53,120 (207.50 KB)
In [52]:
# Define an EarlyStopping callback
early_stopping2 = callbacks.EarlyStopping(
    monitor='val_loss',            # Metric to monitor for early stopping (validation loss)
    patience=5,                    # Number of epochs with no improvement before stopping
    min_delta=1e-7,                # Minimum change in monitored metric to be considered an improvement
    verbose=1,                     # Verbosity level (1 for updates)
    restore_best_weights=True,     # Restore model weights to the best state when stopped
)

# Define a ReduceLROnPlateau callback
reduceLR2 = callbacks.ReduceLROnPlateau(
    monitor='val_loss',            # Metric to monitor for learning rate reduction (validation loss)
    factor=0.8,                    # Factor by which the learning rate will be reduced (e.g., 0.2 means lr *= 0.2)
    patience=1,                    # Number of epochs with no improvement before reducing the learning rate
    min_delta=1e-6,                # Minimum change in monitored metric to trigger a reduction
    cooldown=0,                    # Number of epochs to wait after a reduction before resuming normal operation
    verbose=1                      # Verbosity level (1 for updates)
)
In [53]:
# Train the ResNet50 model using the training and validation datasets
resNet50_hist = resNetModel.fit(
    train_dataset,                            # Training dataset
    batch_size=batch_size,                    # Batch size
    epochs=10,                                # Number of training epochs
    validation_data=validation_dataset,       # Validation dataset
    callbacks=[early_stopping2,reduceLR2]     # Callbacks for early stopping and learning rate reduction
)
Epoch 1/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 598s 5s/step - binary_accuracy: 0.7665 - loss: 1.4458 - val_binary_accuracy: 0.8333 - val_loss: 0.8787 - learning_rate: 0.0010
Epoch 2/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 0s 5s/step - binary_accuracy: 0.8978 - loss: 0.3782
Epoch 2: ReduceLROnPlateau reducing learning rate to 0.000800000037997961.
105/105 ━━━━━━━━━━━━━━━━━━━━ 587s 6s/step - binary_accuracy: 0.8980 - loss: 0.3773 - val_binary_accuracy: 0.8524 - val_loss: 0.9549 - learning_rate: 0.0010
Epoch 3/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 595s 6s/step - binary_accuracy: 0.9392 - loss: 0.1742 - val_binary_accuracy: 0.8381 - val_loss: 0.3648 - learning_rate: 8.0000e-04
Epoch 4/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 601s 6s/step - binary_accuracy: 0.9281 - loss: 0.1829 - val_binary_accuracy: 0.8917 - val_loss: 0.3009 - learning_rate: 8.0000e-04
Epoch 5/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 0s 6s/step - binary_accuracy: 0.9581 - loss: 0.1192
Epoch 5: ReduceLROnPlateau reducing learning rate to 0.0006400000303983689.
105/105 ━━━━━━━━━━━━━━━━━━━━ 628s 6s/step - binary_accuracy: 0.9580 - loss: 0.1194 - val_binary_accuracy: 0.8226 - val_loss: 0.3792 - learning_rate: 8.0000e-04
Epoch 6/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 0s 6s/step - binary_accuracy: 0.9622 - loss: 0.1138
Epoch 6: ReduceLROnPlateau reducing learning rate to 0.0005120000336319208.
105/105 ━━━━━━━━━━━━━━━━━━━━ 623s 6s/step - binary_accuracy: 0.9622 - loss: 0.1139 - val_binary_accuracy: 0.8417 - val_loss: 0.3293 - learning_rate: 6.4000e-04
Epoch 7/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 630s 6s/step - binary_accuracy: 0.9689 - loss: 0.0955 - val_binary_accuracy: 0.9250 - val_loss: 0.2846 - learning_rate: 5.1200e-04
Epoch 8/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 0s 6s/step - binary_accuracy: 0.9691 - loss: 0.1049
Epoch 8: ReduceLROnPlateau reducing learning rate to 0.00040960004553198815.
105/105 ━━━━━━━━━━━━━━━━━━━━ 615s 6s/step - binary_accuracy: 0.9691 - loss: 0.1049 - val_binary_accuracy: 0.8917 - val_loss: 0.6473 - learning_rate: 5.1200e-04
Epoch 9/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 0s 5s/step - binary_accuracy: 0.9727 - loss: 0.0962
Epoch 9: ReduceLROnPlateau reducing learning rate to 0.00032768002711236477.
105/105 ━━━━━━━━━━━━━━━━━━━━ 608s 6s/step - binary_accuracy: 0.9728 - loss: 0.0961 - val_binary_accuracy: 0.2464 - val_loss: 2.7812 - learning_rate: 4.0960e-04
Epoch 10/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 587s 6s/step - binary_accuracy: 0.9766 - loss: 0.0799 - val_binary_accuracy: 0.9012 - val_loss: 0.2659 - learning_rate: 3.2768e-04
Restoring model weights from the end of the best epoch: 10.

Visualisation of the ResNet50 Model's training

In [54]:
# Plotting the results of model training
plt.figure(figsize=(12, 6))

# Plot accuracy curve
plt.subplot(1, 2, 1)
plt.plot(resNet50_hist.history['binary_accuracy'], label='Training Accuracy')
plt.plot(resNet50_hist.history['val_binary_accuracy'], label='Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Training and Validation Accuracy (ResNet50)')
plt.legend()

# Plot loss curve
plt.subplot(1, 2, 2)
plt.plot(resNet50_hist.history['loss'], label='Training Loss')
plt.plot(resNet50_hist.history['val_loss'], label='Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training and Validation Loss (ResNet50)')
plt.legend()

plt.tight_layout()
plt.show()
No description has been provided for this image

Evaluation of the ResNet50 Model

In [55]:
# Evaluate the model on the validation set
resnet_val_loss, resnet_val_acc = resNetModel.evaluate(validation_dataset, verbose=2)
print(f'Validation accuracy: {resnet_val_acc}, Validation loss: {resnet_val_loss}')

# Evaluate the model on the test set
resnet_test_loss, resnet_test_acc = resNetModel.evaluate(X_test, y_test, verbose=2)
print(f'Test accuracy: {resnet_test_acc}, Test loss: {resnet_test_loss}')
27/27 - 27s - 984ms/step - binary_accuracy: 0.9060 - loss: 0.2619
Validation accuracy: 0.9059523940086365, Validation loss: 0.26192528009414673
27/27 - 25s - 939ms/step - binary_accuracy: 0.9595 - loss: 0.1115
Test accuracy: 0.9595237970352173, Test loss: 0.1115061566233635
In [57]:
# Evaluate the model on the test set and obtain predictions
test_predictionsResNet50 = resNetModel.predict(X_test, batch_size=batch_size, verbose=1)

# Convert probability scores to binary predictions (0 or 1)
testProbability_predictionsResNet50 = (test_predictionsResNet50 > 0.5).astype(int)

# Generate a classification report for precision, recall, and F1-score
testClassificationReport_ResNet50 = classification_report(y_test, testProbability_predictionsResNet50, output_dict=True)

# Print the test accuracy, precision, recall, and F1-score
print('Test Accuracy:', testClassificationReport_ResNet50['accuracy'])
print('Test Precision (Overall):', testClassificationReport_ResNet50['macro avg']['precision'])
print('Test Recall (Overall):', testClassificationReport_ResNet50['macro avg']['recall'])
print('Test F1 Score (Overall):', testClassificationReport_ResNet50['macro avg']['f1-score'])
27/27 ━━━━━━━━━━━━━━━━━━━━ 25s 919ms/step
Test Accuracy: 0.9595238095238096
Test Precision (Overall): 0.9689912826899127
Test Recall (Overall): 0.8842857142857143
Test F1 Score (Overall): 0.9201118881118882
In [59]:
# Calculate the confusion matrix between true labels and predicted labels
confusionMatrix_ResNet50 = metrics.confusion_matrix(y_test, testProbability_predictionsResNet50)

# Heatmap of the confusion matrix with annotations
sns.heatmap(confusionMatrix_ResNet50, annot=True, fmt="d", xticklabels=['NORMAL', 'TUBERCULOSIS'], yticklabels=['NORMAL', 'TUBERCULOSIS'])

# Set the x-axis label
plt.xlabel("Predicted Label", fontsize=12)

# Set the y-axis label
plt.ylabel("True Label", fontsize=12)

# Display the heatmap
plt.show()
No description has been provided for this image
In [61]:
# Select a random sample of images to display
num_images_to_display = 10  # Number of images to display
indices = np.random.choice(len(X_test), num_images_to_display, replace=False)

plt.figure(figsize=(15, 10))

for i, idx in enumerate(indices):
    plt.subplot(2, 5, i + 1)
    plt.imshow(X_test[idx], cmap='gray')
    true_label = y_test[idx]
    predicted_label = testProbability_predictionsResNet50[idx]
    color = 'green' if true_label == predicted_label else 'red'
    plt.title(f"True: {true_label} \nPred: {predicted_label}", color=color)
    plt.axis('off')

plt.tight_layout()
plt.show()
No description has been provided for this image

Stacked Model of multiple EfficientNet models and MobileNetV2¶

In [62]:
from tensorflow.keras.applications import EfficientNetB0, EfficientNetB1, EfficientNetB2, MobileNetV2

# Load EfficientNet and MobileNetV2 pre-trained models
baseMEfficientNetB0 = EfficientNetB0(weights='imagenet', include_top=False, input_shape=(244, 244, 3))
baseMEfficientNetB1 = EfficientNetB1(weights='imagenet', include_top=False, input_shape=(244, 244, 3))
baseMEfficientNetB2 = EfficientNetB2(weights='imagenet', include_top=False, input_shape=(244, 244, 3))
baseMMobileNetV2 = MobileNetV2(weights='imagenet', include_top=False, input_shape=(244, 244, 3))

# Freeze convolutional layers for all base models
for model in [baseMEfficientNetB0, baseMEfficientNetB1, baseMEfficientNetB2, baseMMobileNetV2]:
    for layer in model.layers:
        layer.trainable = False

# Define input tensor
input_tensor = Input(shape=(244, 244, 3))

# Extract features using global average pooling layer
efficientnetB0_out = baseMEfficientNetB0(input_tensor)
efficientnetB0_out = GlobalAveragePooling2D()(efficientnetB0_out)

efficientnetB1_out = baseMEfficientNetB1(input_tensor)
efficientnetB1_out = GlobalAveragePooling2D()(efficientnetB1_out)

efficientnetB2_out = baseMEfficientNetB2(input_tensor)
efficientnetB2_out = GlobalAveragePooling2D()(efficientnetB2_out)

mobilenetV2_out = baseMMobileNetV2(input_tensor)
mobilenetV2_out = GlobalAveragePooling2D()(mobilenetV2_out)

# Concatenate features from all models
concFeatures = Concatenate()([efficientnetB0_out, efficientnetB1_out, efficientnetB2_out, mobilenetV2_out])

# Add dense layers for final classification
output = Dense(512, activation='relu')(concFeatures)
output = Dense(1, activation='sigmoid')(output)

# Create the stacked model
stackedModel = Model(inputs=input_tensor, outputs=output, name='Stacked_Model')

# Compile the model
stackedModel.compile(optimizer=Adam(), loss='binary_crossentropy', metrics=['binary_accuracy'])

# Display a summary of the model architecture
stackedModel.summary()
Model: "Stacked_Model"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Layer (type)                  ┃ Output Shape              ┃         Param # ┃ Connected to               ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ input_layer_4 (InputLayer)    │ (None, 244, 244, 3)       │               0 │ -                          │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ efficientnetb0 (Functional)   │ (None, 8, 8, 1280)        │       4,049,571 │ input_layer_4[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ efficientnetb1 (Functional)   │ (None, 8, 8, 1280)        │       6,575,239 │ input_layer_4[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ efficientnetb2 (Functional)   │ (None, 8, 8, 1408)        │       7,768,569 │ input_layer_4[0][0]        │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ mobilenetv2_1.00_224          │ (None, 8, 8, 1280)        │       2,257,984 │ input_layer_4[0][0]        │
│ (Functional)                  │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ global_average_pooling2d      │ (None, 1280)              │               0 │ efficientnetb0[0][0]       │
│ (GlobalAveragePooling2D)      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ global_average_pooling2d_1    │ (None, 1280)              │               0 │ efficientnetb1[0][0]       │
│ (GlobalAveragePooling2D)      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ global_average_pooling2d_2    │ (None, 1408)              │               0 │ efficientnetb2[0][0]       │
│ (GlobalAveragePooling2D)      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ global_average_pooling2d_3    │ (None, 1280)              │               0 │ mobilenetv2_1.00_224[0][0] │
│ (GlobalAveragePooling2D)      │                           │                 │                            │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ concatenate (Concatenate)     │ (None, 5248)              │               0 │ global_average_pooling2d[… │
│                               │                           │                 │ global_average_pooling2d_… │
│                               │                           │                 │ global_average_pooling2d_… │
│                               │                           │                 │ global_average_pooling2d_… │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ dense_3 (Dense)               │ (None, 512)               │       2,687,488 │ concatenate[0][0]          │
├───────────────────────────────┼───────────────────────────┼─────────────────┼────────────────────────────┤
│ dense_4 (Dense)               │ (None, 1)                 │             513 │ dense_3[0][0]              │
└───────────────────────────────┴───────────────────────────┴─────────────────┴────────────────────────────┘
 Total params: 23,339,364 (89.03 MB)
 Trainable params: 2,688,001 (10.25 MB)
 Non-trainable params: 20,651,363 (78.78 MB)
In [63]:
# Train the model
stackedModel_hist = stackedModel.fit(
    train_dataset,                               # Training dataset
    batch_size=batch_size,                       # Batch size
    epochs=10,                                   # Number of training epochs
    validation_data=validation_dataset,          # Validation dataset
    callbacks=[early_stopping, reduceLR]         # Callbacks for early stopping and learning rate reduction
)
Epoch 1/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 363s 3s/step - binary_accuracy: 0.8950 - loss: 0.3185 - val_binary_accuracy: 0.9821 - val_loss: 0.0573 - learning_rate: 0.0010
Epoch 2/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 309s 3s/step - binary_accuracy: 0.9848 - loss: 0.0447 - val_binary_accuracy: 0.9905 - val_loss: 0.0355 - learning_rate: 0.0010
Epoch 3/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 317s 3s/step - binary_accuracy: 0.9831 - loss: 0.0461 - val_binary_accuracy: 0.9940 - val_loss: 0.0241 - learning_rate: 0.0010
Epoch 4/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 306s 3s/step - binary_accuracy: 0.9864 - loss: 0.0356 - val_binary_accuracy: 0.9940 - val_loss: 0.0188 - learning_rate: 0.0010
Epoch 5/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 305s 3s/step - binary_accuracy: 0.9914 - loss: 0.0314 - val_binary_accuracy: 0.9929 - val_loss: 0.0196 - learning_rate: 0.0010
Epoch 6/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 0s 2s/step - binary_accuracy: 0.9891 - loss: 0.0297
Epoch 6: ReduceLROnPlateau reducing learning rate to 0.0005000000237487257.
105/105 ━━━━━━━━━━━━━━━━━━━━ 303s 3s/step - binary_accuracy: 0.9891 - loss: 0.0297 - val_binary_accuracy: 0.9917 - val_loss: 0.0190 - learning_rate: 0.0010
Epoch 7/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 303s 3s/step - binary_accuracy: 0.9899 - loss: 0.0322 - val_binary_accuracy: 0.9917 - val_loss: 0.0204 - learning_rate: 5.0000e-04
Epoch 8/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 0s 2s/step - binary_accuracy: 0.9928 - loss: 0.0153
Epoch 8: ReduceLROnPlateau reducing learning rate to 0.0002500000118743628.
105/105 ━━━━━━━━━━━━━━━━━━━━ 303s 3s/step - binary_accuracy: 0.9928 - loss: 0.0153 - val_binary_accuracy: 0.9845 - val_loss: 0.0470 - learning_rate: 5.0000e-04
Epoch 9/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 305s 3s/step - binary_accuracy: 0.9928 - loss: 0.0185 - val_binary_accuracy: 0.9940 - val_loss: 0.0183 - learning_rate: 2.5000e-04
Epoch 10/10
105/105 ━━━━━━━━━━━━━━━━━━━━ 315s 3s/step - binary_accuracy: 0.9961 - loss: 0.0144 - val_binary_accuracy: 0.9905 - val_loss: 0.0271 - learning_rate: 2.5000e-04
Restoring model weights from the end of the best epoch: 9.

Visualisation of the Stacked Model's training

In [64]:
# Plotting the results of model training
plt.figure(figsize=(12, 6))

# Plot accuracy curve
plt.subplot(1, 2, 1)
plt.plot(stackedModel_hist.history['binary_accuracy'], label='Training Accuracy')
plt.plot(stackedModel_hist.history['val_binary_accuracy'], label='Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Training and Validation Accuracy (Stacked Model)')
plt.legend()

# Plot loss curve
plt.subplot(1, 2, 2)
plt.plot(stackedModel_hist.history['loss'], label='Training Loss')
plt.plot(stackedModel_hist.history['val_loss'], label='Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training and Validation Loss (Stacked Model)')
plt.legend()

plt.tight_layout()
plt.show()
No description has been provided for this image

Evaluation of the Stacked Model

In [65]:
# Evaluate the model on the validation set
stacked_val_loss, stacked_val_acc = stackedModel.evaluate(validation_dataset, verbose=2)
print(f'Validation accuracy: {stacked_val_acc}, Validation loss: {stacked_val_loss}')

# Evaluate the model on the test set
stacked_test_loss, stacked_test_acc = stackedModel.evaluate(X_test, y_test, verbose=2)
print(f'Test accuracy: {stacked_test_acc}, Test loss: {stacked_test_loss}')
27/27 - 63s - 2s/step - binary_accuracy: 0.9929 - loss: 0.0281
Validation accuracy: 0.9928571581840515, Validation loss: 0.028102057054638863
27/27 - 67s - 2s/step - binary_accuracy: 0.9940 - loss: 0.0271
Test accuracy: 0.9940476417541504, Test loss: 0.027139566838741302
In [67]:
# Evaluate the model on the test set and obtain predictions
test_predictionsStackedModel = stackedModel.predict(X_test, batch_size=batch_size, verbose=1)

# Convert probability scores to binary predictions (0 or 1)
testProbability_predictionsStackedModel = (test_predictionsStackedModel > 0.5).astype(int)

# Generate a classification report for precision, recall, and F1-score
testClassificationReport_StackedModel = classification_report(y_test, testProbability_predictionsStackedModel, output_dict=True)

# Print the test accuracy, precision, recall, and F1-score
print('Test Accuracy:', testClassificationReport_StackedModel['accuracy'])
print('Test Precision (Overall):', testClassificationReport_StackedModel['macro avg']['precision'])
print('Test Recall (Overall):', testClassificationReport_StackedModel['macro avg']['recall'])
print('Test F1 Score (Overall):', testClassificationReport_StackedModel['macro avg']['f1-score'])
27/27 ━━━━━━━━━━━━━━━━━━━━ 61s 2s/step
Test Accuracy: 0.9940476190476191
Test Precision (Overall): 0.9964539007092199
Test Recall (Overall): 0.9821428571428572
Test F1 Score (Overall): 0.9891297314784859
In [69]:
# Calculate the confusion matrix between true labels and predicted labels
confusionMatrix_StackedModel = metrics.confusion_matrix(y_test, testProbability_predictionsStackedModel)

# Heatmap of the confusion matrix with annotations
sns.heatmap(confusionMatrix_StackedModel, annot=True, fmt="d", xticklabels=['NORMAL', 'TUBERCULOSIS'], yticklabels=['NORMAL', 'TUBERCULOSIS'])

# Set the x-axis label
plt.xlabel("Predicted Label", fontsize=12)

# Set the y-axis label
plt.ylabel("True Label", fontsize=12)

# Display the heatmap
plt.show()
No description has been provided for this image
In [71]:
# Select a random sample of images to display
num_images_to_display = 10  # Number of images to display
indices = np.random.choice(len(X_test), num_images_to_display, replace=False)

plt.figure(figsize=(15, 10))

for i, idx in enumerate(indices):
    plt.subplot(2, 5, i + 1)
    plt.imshow(X_test[idx], cmap='gray')
    true_label = y_test[idx]
    predicted_label = testProbability_predictionsStackedModel[idx]
    color = 'green' if true_label == predicted_label else 'red'
    plt.title(f"True: {true_label} \nPred: {predicted_label}", color=color)
    plt.axis('off')

plt.tight_layout()
plt.show()
No description has been provided for this image

Basic Averaging Ensemble Model¶

In [73]:
# Define weights for each model
weights = [0.5, 0.5]

# Perform averaging of predictions
average_predictions = ( weights[0]*test_predictionsResNet50 + weights[1]*testProbability_predictionsStackedModel)

# Convert probability scores to binary predictions (0 or 1)
final_predictions = (average_predictions > 0.5).astype(int)

# Generate a classification report for precision, recall, and F1-score
testClassificationReport_AveragingModel = classification_report(y_test, final_predictions, output_dict=True)

# Print the test accuracy, precision, recall, and F1-score
print('Test Accuracy:', testClassificationReport_AveragingModel['accuracy'])
print('Test Precision (Overall):', testClassificationReport_AveragingModel['macro avg']['precision'])
print('Test Recall (Overall):', testClassificationReport_AveragingModel['macro avg']['recall'])
print('Test F1 Score (Overall):', testClassificationReport_AveragingModel['macro avg']['f1-score'])
Test Accuracy: 0.9940476190476191
Test Precision (Overall): 0.9964539007092199
Test Recall (Overall): 0.9821428571428572
Test F1 Score (Overall): 0.9891297314784859
In [74]:
# Calculate the confusion matrix between true labels and predicted labels
confusionMatrix_AveragingModel = metrics.confusion_matrix(y_test, final_predictions)

# Heatmap of the confusion matrix with annotations
sns.heatmap(confusionMatrix_AveragingModel, annot=True, fmt="d")

# Set the x-axis label
plt.xlabel("Predicted Label", fontsize=12)

# Set the y-axis label
plt.ylabel("True Label", fontsize=12)

# Display the heatmap
plt.show()
No description has been provided for this image
In [76]:
# Select a random sample of images to display
num_images_to_display = 10  # Number of images to display
indices = np.random.choice(len(X_test), num_images_to_display, replace=False)

plt.figure(figsize=(15, 10))

for i, idx in enumerate(indices):
    plt.subplot(2, 5, i + 1)
    plt.imshow(X_test[idx], cmap='gray')
    true_label = y_test[idx]
    predicted_label = final_predictions[idx]  # Use the final averaged prediction for display
    color = 'green' if true_label == predicted_label else 'red'
    plt.title(f"True: {true_label} \nPred: {predicted_label}", color=color)
    plt.axis('off')

plt.tight_layout()
plt.show()
No description has been provided for this image

Compare each model to the Averaging Ensembled Model¶

In [77]:
# Define a dictionary with model accuracy scores
accuracyScore_dict = {'ResNet50 Model': round(testClassificationReport_ResNet50['accuracy']*100,2), 
                'Stacked Transfer Learning Model': round(testClassificationReport_StackedModel['accuracy']*100,2), 
                'Averaging Ensemble Model': round(testClassificationReport_AveragingModel['accuracy']*100,2),
                'CNN Model': round(testClassificationReport_CNN['accuracy']*100,2)}

# Convert the dictionary into a Pandas Series
accuracyScore = pd.Series(accuracyScore_dict)

# Sort the models based on their accuracy scores and store the sorted order
order = accuracyScore.sort_values()

order
Out[77]:
ResNet50 Model                     95.95
CNN Model                          97.50
Stacked Transfer Learning Model    99.40
Averaging Ensemble Model           99.40
dtype: float64
In [79]:
# Create dictionaries for each metric
accuracyScore_dict = {
    'CNN Model': round(testClassificationReport_CNN['accuracy']*100, 2), 
    'ResNet50 Model': round(testClassificationReport_ResNet50['accuracy']*100, 2),
    'Stacked Model': round(testClassificationReport_StackedModel['accuracy']*100, 2), 
    'Averaging Ensemble Model': round(testClassificationReport_AveragingModel['accuracy']*100, 2)
}

precisionScore_dict = {
    'CNN Model': round(testClassificationReport_CNN['macro avg']['precision']*100, 2), 
    'ResNet50 Model': round(testClassificationReport_ResNet50['macro avg']['precision']*100, 2),
    'Stacked Model': round(testClassificationReport_StackedModel['macro avg']['precision']*100, 2), 
    'Averaging Ensemble Model': round(testClassificationReport_AveragingModel['macro avg']['precision']*100, 2)
}

recallScore_dict = {
    'CNN Model': round(testClassificationReport_CNN['macro avg']['recall']*100, 2), 
    'ResNet50 Model': round(testClassificationReport_ResNet50['macro avg']['recall']*100, 2),
    'Stacked Model': round(testClassificationReport_StackedModel['macro avg']['recall']*100, 2), 
    'Averaging Ensemble Model': round(testClassificationReport_AveragingModel['macro avg']['recall']*100, 2)
}

f1Score_dict = {
    'CNN Model': round(testClassificationReport_CNN['macro avg']['f1-score']*100, 2), 
    'ResNet50 Model': round(testClassificationReport_ResNet50['macro avg']['f1-score']*100, 2),
    'Stacked Model': round(testClassificationReport_StackedModel['macro avg']['f1-score']*100, 2), 
    'Averaging Ensemble Model': round(testClassificationReport_AveragingModel['macro avg']['f1-score']*100, 2)
}
In [80]:
# Convert dictionaries into a DataFrame
scores_df = pd.DataFrame({
    'Model': list(accuracyScore_dict.keys()),
    'Accuracy': list(accuracyScore_dict.values()),
    'Precision': list(precisionScore_dict.values()),
    'Recall': list(recallScore_dict.values()),
    'F1 Score': list(f1Score_dict.values())
})
In [86]:
# Melt the DataFrame for easier plotting with seaborn
scores_df_melted = scores_df.melt(id_vars='Model', var_name='Metric', value_name='Score')

# Set the style for the plots
sns.set_style('whitegrid')

# Set up the matplotlib figure
plt.figure(figsize=(12, 8))

# Create a bar plot
bar_plot = sns.barplot(x='Score', y='Model', hue='Metric', data=scores_df_melted, palette='coolwarm', orient='h')

# Set labels and title
plt.xlabel('Score (%)', fontsize=14, fontweight='bold')
plt.ylabel('Models', fontsize=14, fontweight='bold')
plt.title('Performance Metrics for Different Models', fontsize=16, fontweight='bold')

# Loop through the bars to annotate each with its value
for p in bar_plot.patches:
    # Format value only if it's greater than zero
    value = p.get_width()
    if value > 0:
        bar_plot.annotate(
            format(value, '.2f') + '%',  # Format to two decimal places and add '%'
            (value, p.get_y() + p.get_height() / 2),  # Annotation position
            ha='left', va='center',  # Horizontal and vertical alignment
            fontsize=10, color='black', weight='bold', xytext=(5, 0), textcoords='offset points'
        )

# Adjust layout to prevent overlapping and move the legend
plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.1), shadow=True, ncol=4)  # Move legend below plot
plt.tight_layout()

# Show the plot
plt.show()
No description has been provided for this image